package info.batcloud.fanli.core.service.impl;

import com.ctospace.archit.common.pagination.Paging;
import info.batcloud.fanli.core.dto.FreeChargeActivityDTO;
import info.batcloud.fanli.core.constants.MessageKeyConstants;
import info.batcloud.fanli.core.entity.*;
import info.batcloud.fanli.core.enums.FreeChargeActivityStatus;
import info.batcloud.fanli.core.repository.CommissionItemRepository;
import info.batcloud.fanli.core.repository.CrowdRepository;
import info.batcloud.fanli.core.repository.FreeChargeActivityRepository;
import info.batcloud.fanli.core.repository.MaterialRepository;
import info.batcloud.fanli.core.service.FreeChargeActivityOrderService;
import info.batcloud.fanli.core.service.FreeChargeActivityService;
import info.batcloud.fanli.core.service.MaterialService;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.inject.Inject;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Predicate;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class FreeChargeActivityServiceImpl implements FreeChargeActivityService {

    @Inject
    private FreeChargeActivityRepository freeChargeActivityRepository;

    @Inject
    private MaterialService materialService;

    @Inject
    private MaterialRepository materialRepository;

    @Inject
    private CrowdRepository crowdRepository;

    @Inject
    private CommissionItemRepository commissionItemRepository;

    @Inject
    private FreeChargeActivityOrderService freeChargeActivityOrderService;

    @Override
    public long createFreeChargeActivity(FreeChargeActivityCreateParam param) {
        FreeChargeActivity packet = new FreeChargeActivity();
        BeanUtils.copyProperties(param, packet);
        packet.setCreateTime(new Date());
        packet.setCrowd(crowdRepository.findOne(param.getCrowdId()));
        packet.setMaterial(materialRepository.findOne(param.getMaterialId()));
        freeChargeActivityRepository.save(packet);
        return packet.getId();
    }

    @Override
    public void updateFreeChargeActivity(long id, FreeChargeActivityUpdateParam param) {
        FreeChargeActivity packet = freeChargeActivityRepository.findOne(id);
        BeanUtils.copyProperties(param, packet);
        packet.setUpdateTime(new Date());
        packet.setCrowd(crowdRepository.findOne(param.getCrowdId()));
        packet.setMaterial(materialRepository.findOne(param.getMaterialId()));
        freeChargeActivityRepository.save(packet);
    }

    @Override
    public void setStatus(long id, FreeChargeActivityStatus status) {
        FreeChargeActivity packet = freeChargeActivityRepository.findOne(id);
        packet.setStatus(status);
        freeChargeActivityRepository.save(packet);
    }

    @Override
    public FreeChargeActivityDTO findById(long id) {
        Date now = new Date();
        return toBO(freeChargeActivityRepository.findByIdAndStartTimeLessThanEqualAndEndTimeGreaterThanEqualAndStatus(id, now, now, FreeChargeActivityStatus.VALID));
    }

    @Override
    public FreeChargeActivityDTO findValidById(long id) {
        return toBO(freeChargeActivityRepository.findOne(id));
    }

    @Override
    public CheckDrawResult checkUserDraw(long userId, long itemId, long activityId) {
        Date now = new Date();
        CheckDrawResult result = new CheckDrawResult();
        FreeChargeActivity freeChargeActivity = freeChargeActivityRepository.findByIdAndStartTimeLessThanEqualAndEndTimeGreaterThanEqualAndStatus(activityId, now, now, FreeChargeActivityStatus.VALID);
        if (freeChargeActivity == null) {
            result.setSuccess(false);
            result.setCode(MessageKeyConstants.FREE_CHARGE_ACTIVITY_STOPPED);
            return result;
        }
        //判断物料是否正确
        if (!materialService.checkCommissionItemInMaterial(freeChargeActivity.getMaterial().getId(), itemId)) {
            result.setSuccess(false);
            result.setCode(MessageKeyConstants.FREE_CHARGE_ACTIVITY_NO_THAT_ITEM);
            return result;
        }
        if (!checkFreq(userId, freeChargeActivity)) {
            result.setSuccess(false);
            result.setCode(MessageKeyConstants.FREE_CHARGE_ACTIVITY_HAS_BEEN_DRAW);
            return result;
        }
        result.setMsg(freeChargeActivity.getConfirmDescription());
        result.setSuccess(true);
        return result;
    }

    @Override
    public CheckDrawResult checkUserDrawToOrder(long userId, long itemId, long activityId) {
        CheckDrawResult result = this.checkUserDraw(userId, itemId, activityId);
        if(result.isSuccess()) {
            FreeChargeActivityOrderService.FreeChargeActivityOrderCreateParam param = new FreeChargeActivityOrderService.FreeChargeActivityOrderCreateParam();
            param.setFreeChargeActivityId(activityId);
            param.setUserId(userId);
            param.setItemId(itemId);
            freeChargeActivityOrderService.createFreeChargeActivityOrder(param);
        }
        return result;
    }

    private boolean checkFreq(long userId, FreeChargeActivity freeChargeActivity) {
        switch (freeChargeActivity.getFreq()) {
            case TIMES:
                return freeChargeActivityOrderService.countFreeChargeActivityByUserIdAndActivityId(userId, freeChargeActivity.getId())
                        < freeChargeActivity.getMaxUserDrawNum();
            case MONTH:
                Date today = new Date();
                Date monthFirstDay = DateUtils.truncate(today, Calendar.MONTH);
                return freeChargeActivityOrderService.countFreeChargeActivityByUserIdAndActivityIdAndCreateTimeBetween(userId,
                        freeChargeActivity.getId(), monthFirstDay, today)
                        < freeChargeActivity.getMaxUserDrawNum();
            case DAY:
                Date now = new Date();
                return freeChargeActivityOrderService.countFreeChargeActivityByUserIdAndActivityIdAndCreateTimeBetween(userId,
                        freeChargeActivity.getId(), DateUtils.truncate(now, Calendar.DATE), now)
                        < freeChargeActivity.getMaxUserDrawNum();
        }
        return false;
    }

    @Override
    public Paging<FreeChargeActivityDTO> search(SearchParam param) {
        Specification<FreeChargeActivity> specification = (root, query, cb) -> {
            Predicate predicate = cb.conjunction();
            List<Expression<Boolean>> expressions = predicate.getExpressions();
            if (param.getStatus() != null) {
                expressions.add(cb.equal(root.get("status"), param.getStatus()));
            } else {
                expressions.add(cb.notEqual(root.get("status"), FreeChargeActivityStatus.DELETED));
            }
            return predicate;
        };
        Sort sort = new Sort(Sort.Direction.DESC, "id");
        Pageable pageable = new PageRequest(param.getPage() - 1,
                param.getPageSize(), sort);
        Page<FreeChargeActivity> page = freeChargeActivityRepository.findAll(specification, pageable);
        List<FreeChargeActivityDTO> dtoList = page.getContent().stream()
                .map(o -> toBO(o)).collect(Collectors.toList());
        return Paging.of(dtoList, Long.valueOf(page.getTotalElements()).intValue(),
                param.getPage(), param.getPageSize());
    }

    private FreeChargeActivityDTO toBO(FreeChargeActivity freeChargeActivity) {
        FreeChargeActivityDTO bo = new FreeChargeActivityDTO();
        BeanUtils.copyProperties(freeChargeActivity, bo);
        Crowd crowd = freeChargeActivity.getCrowd();
        bo.setCrowdId(crowd.getId());
        bo.setCrowdName(crowd.getName());
        Material material = freeChargeActivity.getMaterial();
        bo.setMaterialId(material.getId());
        bo.setMaterialName(material.getName());
        bo.setMaterialConfig(material.getConfig());
        return bo;
    }

}
